package com.dreangine.exception;

import java.util.MissingResourceException;
import java.util.ResourceBundle;

/**
 * @author Omar V. buede
 */
public class DebuggerException extends Exception {
	private static final long serialVersionUID = -2431329576974759740L;
	private ResourceBundle bundle;
	private StackTraceElement stack;
	private StringBuffer errorMessage;
	private boolean debug;
	
	/**
	 * Constructor for use when generating an exception.
	 */
	public DebuggerException() {
		loadBundle();
		this.errorMessage = new StringBuffer();
		this.debug = getDebugFromProperties();
		this.stack = getCorrectStack(this.getStackTrace());
	}
	
	/**
	 * Constructor for use when catching a thrown exception.
	 * 
	 * @param e thrown exception
	 */
	public DebuggerException(Exception e) {
		loadBundle();
		this.errorMessage = new StringBuffer();
		this.debug = getDebugFromProperties();
		this.stack = getCorrectStack(e.getStackTrace());
		this.errorMessage.append(" "); //$NON-NLS-1$
		this.errorMessage.append(e);
	}
	
	/**
	 * Method for checking if debug is enabled or disabled in this instance
	 * of the exception.
	 * 
	 * @return <code>true</code> if debug is enabled and <code>false</code>
	 * if disabled.
	 */
	public boolean isDebug() {
		return this.debug;
	}

	/**
	 * Method for enabling or disabling debug in this instance of the
	 * exception.
	 * 
	 * @param debug <code>true</code> to enable and <code>false</code>
	 * to disable.
	 */
	public void setDebug(boolean debug) {
		this.debug = debug;
	}
	
	/**
	 * Method for loading the properties file.
	 */
	private void loadBundle() {
		try {
			this.bundle = ResourceBundle.getBundle("exceptions"); //$NON-NLS-1$
		} catch (MissingResourceException mre) {
			System.err.println("MISSING FILE exceptions.properties!"); //$NON-NLS-1$
		}
	}
	
	/**
	 * Method used to retrieve a value from the properties file.
	 * 
	 * @param key represents the key from the key-value pair
	 * in the properties file
	 * @return the value from the key-value pair
	 */
	private String getMessage(String key) {
		try {
			return this.bundle.getString(key);
		} catch (Exception e) {
			return null;
		}
	}
	
	/**
	 * Method that searches, in the current stack, for the first element that has
	 * a key in the properties file. If none is found, then the top element is
	 * returned.
	 * 
	 * @param stacks the elements that are stacked
	 * @return the first element that has a key in the properties file or the
	 * top element in the stack
	 */
	private StackTraceElement getCorrectStack(StackTraceElement[] stacks) {
		StackTraceElement currentStack;
		String currentKey, currentMessage;
		
		for (int i = 0; i < stacks.length; i++) {
			currentStack = stacks[i];
			currentKey = currentStack.getClassName() + "." + currentStack.getMethodName(); //$NON-NLS-1$
			
			currentMessage = getMessage(currentKey);
			if(currentMessage != null) {
				this.errorMessage.append(currentMessage);
				return currentStack;
			}
		}
		this.errorMessage.append(getMessage("defaultMessage")); //$NON-NLS-1$
		
		return stacks[0];
	}
	
	/**
	 * Method that retrieves the value of the DEBUG key from the properties
	 * file.
	 * 
	 * @return <code>true</code> or <code>false</code>
	 */
	private boolean getDebugFromProperties() {
		return Boolean.parseBoolean(getMessage("DEBUG")); //$NON-NLS-1$
	}
	
	@Override
	public String toString() {
		StringBuffer message = new StringBuffer();
		
		if(this.debug) {
			message.append("("); //$NON-NLS-1$
			message.append(this.stack.getClassName());
			message.append("."); //$NON-NLS-1$
			message.append(this.stack.getMethodName());
			message.append(" - "); //$NON-NLS-1$
			message.append(getMessage("line")); //$NON-NLS-1$
			message.append(" "); //$NON-NLS-1$
			message.append(this.stack.getLineNumber());
			message.append(") "); //$NON-NLS-1$
		}
		message.append(this.errorMessage);
		
		return message.toString();
	}
}